﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Ditto.Helpers
{
    public class GeolocationHelper
    {
        public struct Coordinate
        {
            public double X;
            public double Y;

            public double XRadians
            {
                get
                {
                    return X / 360 * 2 * Math.PI;
                }
            }

            public double YRadians
            {
                get
                {
                    return Y / 360 * 2 * Math.PI;
                }
            }

            public Coordinate(double x, double y)
            {
                this.X = x;
                this.Y = y;
            }

            public static double GetGradient(Coordinate a1, Coordinate a2)
            {
                return ((a2.Y - a1.Y) / (a2.X - a1.X));

            }

            public static double GetAngle(Coordinate a1, Coordinate mid, Coordinate a2)
            {
                double angle1 = Math.Atan(GetGradient(a1, mid));
                double angle2 = Math.Atan(GetGradient(mid, a2));

                return Math.Abs(angle1 - angle2);
            }

            public override bool Equals(object obj)
            {
                Coordinate c = (Coordinate)obj;

                if (Math.Abs(c.X - X) <= 0.000001 && Math.Abs(c.Y - Y) <= 0.000001)
                    return true;

                return false;
            }

            public override int GetHashCode()
            {
                return X.GetHashCode() ^ Y.GetHashCode();
            }
        }

        public class DistTime
        {
            static double Xscale = 97.4861;
            static double Yscale = 111.317099692187;

            static double limitDistance = 10;
            static double minSpeed = 17;
            static double maxSpeed = 20;
            static double roadCurvatureRatio = 1.3;
            static double earthSphericalRadius = 6378;

            public double Duration = double.MaxValue;
            public double Distance = double.MaxValue;

            public DistTime()
            {

            }

            public DistTime(double duration, double distance)
            {
                this.Duration = duration;
                this.Distance = distance;
            }

            public static DistTime Add(DistTime dt1, DistTime dt2)
            {
                return new DistTime(
                    dt1.Duration + dt2.Duration,
                    dt1.Distance + dt2.Distance);
            }

            public static DistTime Subtract(DistTime dt1, DistTime dt2)
            {
                return new DistTime(
                    dt1.Duration - dt2.Duration,
                    dt1.Distance - dt2.Distance);
            }

            public static DistTime Multiply(DistTime dt1, double factor)
            {
                return new DistTime(
                    dt1.Duration * factor,
                    dt1.Distance * factor);
            }

            public static DistTime GetStraightLineDistTime(Coordinate from, Coordinate to)
            {
                DistTime result = new DistTime();

                if (from.X == to.X && from.Y == to.Y)
                {
                    result.Distance = .01;
                    result.Duration = .01;
                }
                else
                {
                    result.Distance = roadCurvatureRatio * earthSphericalRadius * (Math.Acos(
                        Math.Cos(from.YRadians) *
                        Math.Cos(to.YRadians) *
                        Math.Cos(to.XRadians - from.XRadians)
                        +
                        Math.Sin(from.YRadians) *
                        Math.Sin(to.YRadians)));

                    result.Duration = EstimateDuration(result.Distance);
                }

                return result;
            }

            public static double EstimateDuration(double distance)
            {
                double velocity = 0;
                if (distance < limitDistance)
                    velocity = minSpeed;
                else
                    velocity = maxSpeed;

                return distance / velocity;
            }

            private static DistTime GetStraightLineDistTime_Old(Coordinate from, Coordinate to)
            {
                DistTime result = new DistTime();
                result.Distance = Math.Sqrt(Math.Pow((to.X - from.X) * Xscale, 2) + Math.Pow((to.Y - from.Y) * Yscale, 2));
                result.Duration = EstimateDuration(result.Distance);

                return result;
            }
        }
    }
}